﻿using BlockOS;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ZerAx.ObjectNotation;
using ZerAx.Tweaks.Collections;

namespace BlockOS
{
    public class ChunkData
    {
        public Palette3D<BlockData> blocks;
        private List<BlockEntity> blockEntities;

        private bool m_dirty;

        public ChunkData()
        {
            blocks = new Palette3D<BlockData>(Consts.Chunk.SIZE, Consts.Chunk.SIZE, Consts.Chunk.SIZE);
            blockEntities = new List<BlockEntity>();

            m_dirty = true;
        }

        public Zon ToZon() => Zon.Compound(
            ("indices", Zon.I32Array(blocks.indices.Select(x => x == null ? 0 : x.Value + 1).ToArray())),
            ("block_states", Zon.List(blocks.Values.Select(x => x.ToZon())))
        );

        public static ChunkData LoadFromZonDocument(ZonCompound dom)
        {
            var result = new ChunkData();

            if (dom["indices"] is not ZonI32Array indicesArr) return result;
            if (dom["block_states"] is not ZonList blockStates) return result;

            List<BlockData> blocks = new();
            for (var i = 0; i < blockStates.data.Count; i++)
            {
                blocks.Add(new BlockData(blockStates.data[i] as ZonCompound));
            }
            int?[] indices = new int?[result.blocks.indices.Length];
            for (var i = 0; i < indicesArr.data.Count; i++)
            {
                var index = indicesArr.data[i];
                indices[i] = index == 0 ? null : index - 1;
                if (indices[i] is int val)
                {
                    result.AddBlock(blocks[val], 0, 0, 0);
                }
            }
            return result;
        }

        public void Clear()
        {
            blocks.Clear();
            blockEntities.Clear();
            m_dirty = true;
        }

        public BlockData GetBlockData(int x, int y, int z)
        {
            return blocks[x, y, z];
        }

        public void AddBlock(BlockData data, int x, int y, int z)
        {
            m_dirty = true;
            blocks[x, y, z] = data;
        }

        public void BatchedAddBlock(BlockData data, IEnumerable<(int, int, int)> positions)
        {
            m_dirty = true;
            blocks.BatchedSet(positions, data);
        }

        public void RemoveBlock(int x, int y, int z)
        {
            m_dirty = true;
            blocks.Remove(x, y, z);
        }

        public bool IsDirty => m_dirty;
        public void UnmarkDirty()
        {
            m_dirty = false;
        }

        public IEnumerable<BlockData> BlockDatas => blocks.Values;

        public bool NeedRebuildModels => blocks.IsDirty;
        public void MarkRebuild() => blocks.UnmarkDirty();
    }
}